using System;
using BusinessLogic.BusinessEntity.Entities;
using BusinessLogic.Managers.Processing.ProcessingExceptions;
using BusinessLogic.Managers.Processing.RemittanceAdvice.Exceptions;
using BusinessLogic.Mappers;
using DataAccess.Services;

namespace BusinessLogic.Managers.Processing.RemittanceAdvice.ValidationRules
{
	internal class RemittanceAdviceAccountValidationRule : IRemittanceAdviceValidationRule
	{
		public void Validate(BusinessEntity.Entities.RemittanceAdvice remittanceAdvice, IServiceManager serviceManager)
		{
			ValidateAccountExistance(remittanceAdvice, serviceManager, RemittanceAdviceAccountType.BillTo);
			ValidateAccountExistance(remittanceAdvice, serviceManager, RemittanceAdviceAccountType.Payer);

			ValidateAccountCodesClients(remittanceAdvice.PayerPartyAccount.Number, remittanceAdvice.BillToPartyAccount.Number, serviceManager);
		}

		private static void ValidateAccountExistance(BusinessEntity.Entities.RemittanceAdvice remittanceAdvice, IServiceManager serviceManager, RemittanceAdviceAccountType accountType)
		{
			Account accountToValidate;

			switch (accountType)
			{
				case RemittanceAdviceAccountType.BillTo:
					accountToValidate = remittanceAdvice.BillToPartyAccount;
					break;
				case RemittanceAdviceAccountType.Payer:
					accountToValidate = remittanceAdvice.PayerPartyAccount;
					break;
				default:
					throw GetNotSupportedAccountTypeException(accountType);
			}

			if (!MapperFactory.GetAccountMapper(serviceManager).Exists(accountToValidate.Number))
			{
				throw new AccountCodeDoesNotExistException(accountToValidate.Number);
			}

			Account loadedAccount = MapperFactory.GetAccountMapper(serviceManager).GetAccountByNumber(accountToValidate.Number);
			loadedAccount.Currency = MapperFactory.GetCurrencyMapper(serviceManager).GetById(loadedAccount.Currency.ID);

			switch (accountType)
			{
				case RemittanceAdviceAccountType.BillTo:
					remittanceAdvice.BillToPartyAccount = loadedAccount;
					break;
				case RemittanceAdviceAccountType.Payer:
					remittanceAdvice.PayerPartyAccount = loadedAccount;
					break;
				default:
					throw GetNotSupportedAccountTypeException(accountType);
			}
		}

		private static NotSupportedException GetNotSupportedAccountTypeException(RemittanceAdviceAccountType accountType)
		{
			return new NotSupportedException(string.Format("Remittance Advice accounts of type {0} are not supported", accountType));
		}

		private static void ValidateAccountCodesClients(string payerAccountCode, string billToAccountCode, IServiceManager serviceManager)
		{
			var clientMapper = MapperFactory.GetUserMapper(serviceManager);

			if (clientMapper.GetByAccountCode(payerAccountCode).ID == clientMapper.GetByAccountCode(billToAccountCode).ID)
			{
				throw new PayerAndBillToAccountCodesForRemittanceAdviceLeadToTheSameClientException();
			}
		}

		enum RemittanceAdviceAccountType
		{
			Payer,
			BillTo
		}
	}
}